﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using CCAI.Office.PowerPoint.TimedText.Custom;
using TimedText;
using TimedText.Timing;

namespace CCAI.Office.PowerPoint.TimedText
{
    /// <summary>
    /// Accepts a string of XML defining the markers and returns a collection of MediaMarker objects.
    /// </summary>
    /// <remarks>
    /// For more information about Timed Text Authoring Format 1.0, 
    /// see http://www.w3.org/TR/2009/WD-ttaf1-dfxp-20090602/#dfxp-example-subtitle-1
    /// </remarks>
    public class TimedTextMarkerParserCustom 
    {
        public IEnumerable<TimedTextElementCustom> GetTimedTextElementCollection(XDocument markerXml, TimeSpan timeOffset, TimeSpan defaultEndTime)
        {
            try
            {
                TtElement document = BuildDocument(markerXml, timeOffset, defaultEndTime);
                var listElements = BuildTimedTextElementCustom(document);

                return listElements;
            }
            catch (Exception ex)
            {
                throw new ArgumentException("TimedTextMarkerParser.ParseMarkerCollection received invalid input.", ex);
            }
        }

        private static TtElement BuildDocument(XDocument xml, TimeSpan timeOffset, TimeSpan defaultEndTime)
        {
            TtElement parsetree = null;

            try
            {
                parsetree = (TtElement) TimedTextElementBase.Parse(xml.Root);
            }
            catch (TimedTextException)
            {
            }

            if (parsetree == null) throw new TimedTextException("No Parse tree returned");
            if (!parsetree.Valid()) throw new TimedTextException("Document is Well formed XML, but invalid Timed Text");

            var startTime = new TimeCode(timeOffset, TimeExpression.CurrentSmpteFrameRate);
            var endTime = new TimeCode(defaultEndTime, TimeExpression.CurrentSmpteFrameRate);
            parsetree.ComputeTimeIntervals(TimeContainer.Par, startTime, endTime);

            return parsetree;
        }

        private static IEnumerable<TimedTextElementCustom> BuildTimedTextElementCustom(TtElement document)
        {
            var regionElementsHash = document.Head != null
                                         ? document.Head.Children.Where(i => i is LayoutElement)
                                               .SelectMany(i => i.Children)
                                               .Cast<RegionElement>()
                                               .ToDictionary(i => i.Id, i => i)
                                         : new Dictionary<string, RegionElement>();

            //var captionRegionsHash = regionElementsHash.Values
            //    .Select(MapToCaptionRegionCustom)
            //    .ToDictionary(i => i.Id, i => i);
            IList<TimedTextElementCustom> list = new List<TimedTextElementCustom>();

            if (document.Body != null)
            {
                BuildCaptionsCustom(document.Body, regionElementsHash, list);
            }

            return list;
        }
        
        private static void BuildCaptionsCustom(TimedTextElementBase timedTextElement,
                                          IDictionary<string, RegionElement> regionElementsHash, IList<TimedTextElementCustom> listElements)
        {
            var pElement = timedTextElement as PElement;

            if (pElement != null)
            {
                var regionNameAttribute = pElement.Attributes.FirstOrDefault(i => i.LocalName == "region");
                var styleNameAtrribute = pElement.Attributes.FirstOrDefault(i => i.LocalName == "style");
                var regionName = regionNameAttribute != null ? regionNameAttribute.Value : "default region"; //string.Empty;
                var styleName = styleNameAtrribute != null ? styleNameAtrribute.Value : string.Empty;

                //StyleElement styleElement;
                //if (styleElementsHash.TryGetValue(styleName, out styleElement))
                //{
                //    var captionElement = MapToCaptionCustom(pElement,null, styleElement);

                //    CaptionRegion captionRegion;
                //    if (captionRegionsHash.TryGetValue(regionName, out captionRegion))
                //    {
                //       // captionElement.Index = captionRegion.Children.Count;
                //        captionRegion.Children.Add(styleElement);
                //    }
                //}

                RegionElement regionElement;
                if (regionElementsHash.TryGetValue(regionName, out regionElement))
                {
                    var captionElement = MapToCaptionCustom(pElement, regionElement, null);
                    listElements.Add(captionElement);
                  
                    //if (captionRegionsHash.TryGetValue(regionName, out captionRegion))
                    //{
                    //   // captionElement.Index = captionRegion.Children.Count;
                    //    captionRegion.Children.Add(captionElement);
                    //}
                }
            }
            else if (timedTextElement.Children != null)
            {
                timedTextElement.Children
                    .Cast<TimedTextElementBase>()
                    .ForEach(i => BuildCaptionsCustom(i, regionElementsHash, listElements));
            }
        }

        private static CaptionElementCustom MapToCaptionRegionCustom(RegionElement regionElement)
        {
            var endTime = regionElement.End.TotalSeconds >= TimeSpan.MaxValue.TotalSeconds
                              ? TimeSpan.MaxValue
                              : TimeSpan.FromSeconds(regionElement.End.TotalSeconds);

            //note this is where it comes in when mapping regions back to a style element
            var captionRegion = new CaptionElementCustom
                                    {
                                        Id = regionElement.Id,
                                        Begin = TimeSpan.FromSeconds(regionElement.Begin.TotalSeconds),
                                        End = endTime,
                                        Style = TimedTextStyleParserCustom.MapStyle(regionElement, null)
                                    };

            foreach (TimedTextElementBase element in regionElement.Children)
            {
                TimedTextElementCustom  child = BuildTimedTextElementsCustom(element, null);
                if (child != null && child.ElementType == TimedTextElementTypeCustom.Animation)
                {
                    //captionRegion.Animations.Add(child as TimedTextAnimation);
                    //todo override the style
                    captionRegion.Style = child.Style;
                }
            }

            return captionRegion;
        }

        private static CaptionElementCustom MapToCaptionCustom(PElement pElement, RegionElement region, StyleElement styleElement)
        {
            var captionElement = BuildTimedTextElementsCustom(pElement, region);
            captionElement.Id = pElement.Id ?? Guid.NewGuid().ToString();

            return captionElement as CaptionElementCustom;
        }
       
        private static TimedTextElementCustom BuildTimedTextElementsCustom(TimedTextElementBase element, RegionElement region)
        {
            TimedTextElementCustom timedTextElement = CreateTimedTextElementCustom(element, region);
            var captionElement = (CaptionElementCustom)timedTextElement;
            foreach (TimedTextElementBase c in element.Children)
            {
                TimedTextElementCustom child = BuildTimedTextElementsCustom(c, region);
               /* if (child is TimedTextAnimationCustom)
                {
                    //timedTextElement.Animations.Add((TimedTextAnimation)child);
                    //timedTextElement.Style = child.Style;
                    timedTextElement.CalculateCurrentStyle(child);
                }
                else */
                if (captionElement != null && child is CaptionElementCustom)
                {
                    //captionElement.Children.Add((CaptionElement)child);
                    captionElement.Content += child.Content;
                    captionElement.Style = child.Style;
                    captionElement.ElementType = child.ElementType;
                }
            }
            return timedTextElement;
        }
        private static TimedTextElementCustom CreateTimedTextElementCustom(TimedTextElementBase element, RegionElement region)
        {
            var captionElement = /* element is SetElement
                                    ? (TimedTextElementCustom)BuildCaptionAnimationElementCustom(element)
                                    :*/ new CaptionElementCustom();

            captionElement.End = TimeSpan.FromSeconds(element.End.TotalSeconds);
            captionElement.Begin = TimeSpan.FromSeconds(element.Begin.TotalSeconds);

            if (element is BrElement)
            {
                captionElement.ElementType = TimedTextElementTypeCustom.LineBreak;
            }
            else if (element is AnonymousSpanElement)
            {
                var span = element as AnonymousSpanElement;
                captionElement.ElementType = TimedTextElementTypeCustom.Text;
                captionElement.Content = span.Text;
                captionElement.Style = TimedTextStyleParserCustom.MapStyle(element, region);
            }
            else if (!(element is SetElement))
            {
                captionElement.ElementType = TimedTextElementTypeCustom.Container;
                captionElement.Style = TimedTextStyleParserCustom.MapStyle(element, region);
            }

            return captionElement;
        }

     
    }
}